package com.kyl.base;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.github.pagehelper.Page;
import com.kyl.utils.ConvertHandler;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
/**
 * 分页结果包装类，用于统一分页数据的响应格式
 * @author WanJl
 * @version 1.0
 * @title PageResponse
 * @description 分页数据消息体，包含分页信息和数据列表
 * @create 2024/12/25
 */
@Data
@ApiModel(value = "分页数据消息体", description = "分页数据统一对象")
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor
public class PageResponse<T> {

    @ApiModelProperty(value = "总条目数", required = true)
    private Long total = 0L;

    @ApiModelProperty(value = "页尺寸", required = true)
    private Integer pageSize = 0;

    @ApiModelProperty(value = "总页数", required = true)
    private Long pages = 0L;

    @ApiModelProperty(value = "页码", required = true)
    private Integer page = 0;

    @ApiModelProperty(value = "数据列表", required = true)
    private List<T> records = Collections.EMPTY_LIST;
    /**
     * 通过mybatis的分页对象构造分页响应，不包含数据列表
     *
     * @param page 分页对象，用于获取分页信息
     */
    public PageResponse(Page<?> page) {
        this.page = Convert.toInt(page.getPageNum());
        this.total = page.getTotal();
        this.pageSize = Convert.toInt(page.getPageSize());
        this.pages = (long) page.getPages();
    }

    /**
     * 通过mybatis的分页对象构造分页响应，包含数据列表
     *
     * @param page  分页对象，用于获取分页信息和数据列表
     * @param clazz 指定数据列表项的类型
     */
    public PageResponse(Page<?> page, Class<T> clazz) {
        this.page = Convert.toInt(page.getPageNum());
        this.total = page.getTotal();
        this.pageSize = Convert.toInt(page.getPageSize());
        this.pages = (long) page.getPages();
        // 本构造函数中未处理records，因为需要类型转换，将在其他方法中实现
    }

    /**
     * 返回一个空的分页响应实例
     *
     * @return 分页数据对象实例，所有字段为默认值
     */
    public static <T> PageResponse<T> getInstance() {
        return PageResponse.<T>builder().build();
    }

    /**
     * 将Page对象封装为PageResponse，不包含数据列表
     *
     * @param page 源分页对象，用于获取分页信息
     * @return 分页数据对象，包含分页信息但不包含数据列表
     */
    public static <T> PageResponse<T> of(Page<?> page) {
        return PageResponse.<T>builder()
                .page(Convert.toInt(page.getPageNum()))
                .pageSize(Convert.toInt(page.getPageSize()))
                .pages((long) page.getPages())
                .total(page.getTotal())
                .build();
    }

    /**
     * 将Page对象封装为PageResponse，并将数据列表转换为指定类型
     *
     * @param page 源分页对象，用于获取分页信息和数据列表
     * @param clazz 指定数据列表项的类型
     * @return 分页数据对象，包含分页信息和转换后的数据列表
     */
    public static <T> PageResponse<T> of(Page<?> page, Class<T> clazz) {
        return of(page, clazz, null);
    }

    /**
     * 将Page对象封装为PageResponse，并将数据列表转换为指定类型，支持自定义转换逻辑
     *
     * @param page           源分页对象，用于获取分页信息和数据列表
     * @param clazz          指定数据列表项的类型
     * @param convertHandler 自定义转换处理器，用于处理特殊类型的转换
     * @return 分页数据对象，包含分页信息和转换后的数据列表
     */
    public static <O, T> PageResponse<T> of(Page<O> page, Class<T> clazz, ConvertHandler<O, T> convertHandler) {
        return PageResponse.<T>builder()
                .page(Convert.toInt(page.getPageNum()))
                .pageSize(Convert.toInt(page.getPageSize()))
                .pages((long) page.getPages())
                .total(page.getTotal())
                .records(copyToList(page.getResult(), clazz, convertHandler))
                .build();
    }

    /**
     * 将PageResponse对象转换为另一个类型的数据列表
     *
     * @param origin 源分页数据对象，包含分页信息和数据列表
     * @param clazz  指定数据列表项的类型
     * @return 新的分页数据对象，包含转换后的数据列表
     */
    public static <O, T> PageResponse<T> of(PageResponse<O> origin, Class<T> clazz) {
        return of(origin, clazz, null);
    }

    /**
     * 将PageResponse对象转换为另一个类型的数据列表，支持自定义转换逻辑
     *
     * @param origin         源分页数据对象，包含分页信息和数据列表
     * @param clazz          指定数据列表项的类型
     * @param convertHandler 自定义转换处理器，用于处理特殊类型的转换
     * @return 新的分页数据对象，包含转换后的数据列表
     */
    public static <O, T> PageResponse<T> of(PageResponse<O> origin, Class<T> clazz, ConvertHandler<O, T> convertHandler) {
        PageResponse<T> target = PageResponse.getInstance();
        BeanUtil.copyProperties(origin, target, "items");

        if (CollUtil.isEmpty(origin.getRecords())) {
            return target;
        }
        List<T> targetList = copyToList(origin.getRecords(), clazz, convertHandler);
        target.setRecords(targetList);

        return target;
    }

    /**
     * 将List对象封装为分页数据对象
     *
     * @param items    数据列表
     * @param page     页码，如果为空，默认为1
     * @param pageSize 页尺寸，如果为空，默认为1
     * @param pages    总页数，如果为空，默认为1
     * @param counts   总条目数，如果为空，默认为1
     * @return 分页数据对象，包含分页信息和数据列表
     */
    public static <T> PageResponse<T> of(List<T> items, Integer page, Integer pageSize, Long pages, Long counts) {
        PageResponse<T> pageResponse = PageResponse.<T>builder()
                .page(Optional.ofNullable(page).orElse(1))
                .pageSize(Optional.ofNullable(pageSize).orElse(1))
                .pages(Optional.ofNullable(pages).orElse(1L))
                .total(Optional.ofNullable(counts).orElse(1L))
                .build();

        if (CollUtil.isEmpty(items)) {
            return pageResponse;
        }

        pageResponse.setRecords(items);
        return pageResponse;
    }

    /**
     * 将List对象封装为分页数据对象，所有分页字段默认为1
     *
     * @param items 数据列表
     * @return 分页数据对象，包含默认分页信息和数据列表
     */
    public static <T> PageResponse<T> of(List<T> items) {
        return of(items, null, null, null, null);
    }

    /**
     * 返回包含任意数量元素的分页对象，所有分页字段默认为1
     *
     * @param elements 数据列表的元素
     * @return 分页数据对象，包含默认分页信息和数据列表
     */
    @SafeVarargs
    public static <E> PageResponse<E> of(E... elements) {
        return of(List.of(elements));
    }

    /**
     * 使用自定义函数将数据列表转换为另一个类型的数据列表
     *
     * @param origin 源分页数据对象，包含分页信息和数据列表
     * @param function 自定义转换函数，用于将源数据列表转换为目标类型的数据列表
     * @return 新的分页数据对象，包含转换后的数据列表
     */
    public static <O, T> PageResponse<T> of(PageResponse<O> origin, Function<List<O>, List<T>> function) {
        List<T> orderVOList = function.apply(origin.getRecords());
        return PageResponse.of(orderVOList, origin.getPage(), origin.getPageSize(), origin.getPages(), origin.total);
    }

    /**
     * 将数据列表转换为另一个类型的数据列表，支持自定义转换逻辑
     *
     * @param content 源数据列表
     * @param clazz   目标数据列表项的类型
     * @param convertHandler 自定义转换处理器，用于处理特殊类型的转换
     * @param <T> 目标数据列表项的类型
     * @param <O> 源数据列表项的类型
     * @return 转换后的数据列表
     */
    private static <T, O> List<T> copyToList(List<O> content, Class<T> clazz, ConvertHandler<O, T> convertHandler) {
        List<T> targetList = BeanUtil.copyToList(content, clazz);

        if (CollUtil.isNotEmpty(targetList) && ObjectUtil.isNotEmpty(convertHandler)) {
            for (int i = 0; i < content.size(); i++) {
                convertHandler.map(content.get(i), targetList.get(i));
            }
        }
        return targetList;
    }
}
